using FSM.Dal;
using FSM.IDal;
using FSM.IBll;
using FSM.Model;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Text;
using Util.Excel;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using System.IO.Compression;
using Util.Extensions;

namespace FSM.Bll
{
    public class TbFileBll : ITbFileBll
    {
        #region 模块 
        private readonly ITbFileDal _dal;
        private readonly ITbFileRestoreDal _restoreDal;
        private readonly ITbFilePredeleteBll _preDeleteBll;
        private readonly ITbFileLogBll _fileLogBll;
        private readonly IExcelAssistant _excel;
        public TbFileBll(ITbFileDal dal, ITbFilePredeleteBll preDeleteBll, ITbFileLogBll fileLogBll, ITbFileRestoreDal restoreDal, IExcelAssistant excel)
        {
            _dal = dal;
            _restoreDal = restoreDal;
            _preDeleteBll = preDeleteBll;
            _fileLogBll = fileLogBll;
            _excel = excel;
        }
        #endregion

        #region 查询 

        /// <summary> 
        /// 根据键值查询单个信息 
        /// </summary> 
        /// <param name="id">键值</param> 
        /// <returns></returns> 
        public tb_file Get(int id)
        {
            return _dal.GetById(id);
        }

        /// <summary>
        /// 根据文件/文件夹姓名获得文件信息
        /// </summary>
        /// <param name="upid">上级文件夹ID</param>
        /// <param name="name">文件/文件夹名称</param>
        /// <returns></returns>
        public tb_file GetByName(int upid, string name)
        {
            return _dal.GetList(p => p.upid == upid && p.name == name)
                       .FirstOrDefault();
        }

        /// <summary>
        /// 根据虚拟目录获得文件信息
        /// </summary>
        /// <param name="file">被删除的文件信息</param>
        /// <returns></returns>
        public tb_file GetByVirtualDir(tb_file_predelete file)
        {
            string virtualDir = file.virtual_dir;
            List<tb_file> fileList = List(file.dir, null, file.name, null, file.level);
            foreach (tb_file temp in fileList)
            {
                string virtualPath = GetVirtualDir(temp.upid.Value);
                if (virtualPath.Equals(virtualDir, StringComparison.CurrentCultureIgnoreCase))
                {
                    return temp;
                }
            }
            return null;
        }

        /// <summary> 
        /// 分页查询批量信息 
        /// </summary> 
        /// <param name="dir">文件/文件夹所在目录</param>
        /// <param name="upid">上级键值</param> 
        /// <param name="name">名称</param> 
        /// <param name="type">1：文件夹 2：文件</param> 
        /// <param name="level">文件/文件夹的层级</param>
        /// <param name="orderName">排序名称</param>
        /// <param name="orderType">
        /// 排序类型
        /// null: 默认排序
        /// 0：升序
        /// 1：降序
        /// </param>
        /// <param name="page">页码</param> 
        /// <param name="page_size">每页显示的条数</param> 
        /// <returns></returns> 
        public Tuple<List<tb_file>, int> ListPagin(string dir, int? upid, string name, int? type, ushort? level, string orderName, int? orderType, int page, int page_size)
        {
            ISugarQueryable<tb_file> queryable = _dal.CreateQueryable();
            queryable = queryable.WhereIF(!string.IsNullOrEmpty(dir), p => p.dir == dir);
            queryable = queryable.WhereIF(upid != null, p => p.upid == upid);
            queryable = queryable.WhereIF(!string.IsNullOrEmpty(name), p => p.name.Contains(name));
            queryable = queryable.WhereIF(type != null, p => p.type == type);
            queryable = queryable.WhereIF(level != null, p => p.level == level.Value);
            if (!string.IsNullOrEmpty(orderName) && orderType != null)
            {
                OrderByType obt = orderType.Value == 0 ? OrderByType.Asc : OrderByType.Desc;
                switch (orderName)
                {
                    case "name":
                        queryable = queryable.OrderBy(p => p.name, obt);
                        break;
                    case "size":
                        queryable = queryable.OrderBy(p => p.size, obt);
                        break;
                    case "date":
                        queryable = queryable.OrderBy(p => p.edit_date, obt);
                        break;
                    default:
                        queryable = queryable.OrderBy(p => p.id, OrderByType.Asc);
                        break;
                }
            }
            else
            {
                queryable = queryable.OrderBy(p => p.id, OrderByType.Asc);
            }

            PageModel pm = new PageModel
            {
                PageIndex = page,
                PageSize = page_size
            };
            Tuple<List<tb_file>, int> result = _dal.GetPageListAndTotal(queryable, pm);
            return result;
        }

        /// <summary> 
        /// 分页查询批量信息 
        /// </summary> 
        /// <param name="dir">文件/文件夹所在目录</param>
        /// <param name="upid">上级键值</param> 
        /// <param name="name">名称</param> 
        /// <param name="type">1：文件夹 2：文件</param> 
        /// <param name="level">文件/文件夹的层级</param>
        /// <returns></returns> 
        public List<tb_file> List(string dir, int? upid, string name, int? type, ushort? level)
        {
            Expressionable<tb_file> queryable = Expressionable.Create<tb_file>();
            queryable = queryable.AndIF(!string.IsNullOrEmpty(dir), p => p.dir == dir);
            queryable = queryable.AndIF(upid != null, p => p.upid == upid);
            queryable = queryable.AndIF(!string.IsNullOrEmpty(name), p => p.name.Contains(name));
            queryable = queryable.AndIF(type != null, p => p.type == type);
            queryable = queryable.AndIF(level != null, p => p.level == level.Value);

            List<tb_file> result = _dal.GetList(queryable.ToExpression())
                .OrderBy(p => p.id)
                .ToList();
            return result;
        }

        /// <summary>
        /// 获得文件夹信息及其子文件夹及子文件信息
        /// </summary>
        /// <param name="id">文件夹ID</param>
        /// <returns></returns>
        public tb_file ListCascade(int id)
        {
            tb_file root = Get(id);
            IterationFiles(root);
            return root;
        }

        /// <summary>
        /// 迭代文件及文件夹信息
        /// </summary>
        /// <param name="up">上级的文件夹信息</param>
        private void IterationFiles(tb_file up)
        {
            up.children = List("", up.id, null, null, null);
            foreach (var sub in up.children)
            {
                if (sub.type == 1)
                {
                    IterationFiles(sub);
                }
            }
        }

        /// <summary>
        /// 获得子项的数量
        /// </summary>
        /// <param name="upid">上级ID</param>
        /// <returns></returns>
        public int GetSubCount(int upid)
        {
            return _dal.GetCount(p => p.upid == upid);
        }

        /// <summary>
        /// 判断文件是否存在
        /// </summary>
        /// <param name="upid">上级文件夹ID</param>
        /// <param name="name">文件名称</param>
        /// <returns></returns>
        public bool Exists(int upid, string name)
        {
            return _dal.GetCount(p => p.upid == upid && p.name == name) > 0;
        }

        #endregion

        #region 提交 

        /// <summary> 
        /// 添加新的文件/文件夹
        /// 判断文件/文件夹的名称在当前文件夹下是否存在
        /// 如果存在，则判断是否是点击新增按钮的操作
        ///           如果是，判断文件夹的名称是否是【新增文件夹】
        ///                   如果是则查询【新增文件夹[index]】中最大的index，在index后页加1
        ///           如果不是则抛出异常信息
        /// 如果不存在，则进行插入操作
        /// </summary> 
        /// <param name="model">实体</param> 
        /// <returns></returns> 
        public bool Insert(tb_file model)
        {
            bool success = false;
            _dal.BeginTran();
            try
            {
                int count = _dal.GetCount(p => p.upid == model.upid && p.name == model.name);
                if (count > 0)
                {
                    if (model.name == "新增文件夹")
                    {
                        string name = _dal.GetSimpleClient().AsQueryable().Where(p => p.upid == model.upid && p.name.StartsWith(model.name)).Max(p => p.name);
                        string strIndex = name.Replace(model.name, "", StringComparison.CurrentCultureIgnoreCase);
                        int index = 0;
                        int.TryParse(strIndex, out index);
                        model.name = $"{model.name}{index + 1}";
                    }
                    else
                    {
                        throw new Exception($"ADD01：文件或文件夹【{model.name}】已存在");
                    }
                }

                model.create_date = DateTime.Now;
                int id = _dal.InsertReturnIdentity(model);
                model.id = id;

                /* ******
                 * 如果文件类型为文件，则要更新所在文件夹的大小
                 * 同时级联更新上级文件的大小
                 */
                if (model.upid > 0 && model.type == 2)
                {
                    success = UpdateUpFolderSize(model.creater, model.upid.Value, model.create_date.Value);
                    if (success == false)
                    {
                        throw new Exception("ADD03：更新文件夹大小失败");
                    }
                }

                //插入文件操作日志
                tb_file_log opLog = new tb_file_log()
                {
                    file_id = model.id,
                    oper = model.creater,
                    op_date = model.create_date,
                    op_content = $"创建了{(model.type == 1 ? "文件夹" : "文件")}"
                };
                success = _fileLogBll.Insert(opLog);
                if (!success)
                {
                    throw new Exception($"ADD04：{model.name}新增失败");
                }

                _dal.CommitTran();
                success = true;
            }
            catch (Exception)
            {
                _dal.RollbackTran();
                success = false;
                throw;
            }
            return success;
        }

        /// <summary>
        /// 更新所在文件夹的大小
        /// </summary>
        /// <param name="oper">操作人员</param>
        /// <param name="upid">所在文件夹ID</param>
        /// <param name="date">变动日期</param>
        /// <returns></returns>
        public bool UpdateUpFolderSize(string oper, int upid, DateTime date)
        {
            decimal size = _dal.GetSimpleClient()
                               .AsQueryable()
                               .Where(p => p.upid == upid)
                               .Sum(p => p.size);
            bool success = _dal.UpdateIncludeColumns(new tb_file()
            {
                id = upid,
                size = size,
                editer = oper,
                edit_date = date
            }, "size", "editer", "edit_date") == 1;
            if (success)
            {
                tb_file file = Get(upid);
                if (file.upid > 0)
                {
                    success = UpdateUpFolderSize(oper, file.upid.Value, date);
                    if (!success)
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        /// <summary> 
        /// 更新 
        /// </summary> 
        /// <param name="model">实体</param> 
        /// <returns></returns> 
        public bool Update(tb_file model)
        {
            model.edit_date = DateTime.Now;
            return _dal.UpdateExcludeColumns(model, "creater", "create_date") == 1;
        }

        /// <summary>
        /// 更新文件/文件夹名称
        /// 
        /// 先查询新的名称是否与再有名称一致，如果一致则不进行操作
        /// 如果名称不一致，判断当前名称是否已存在
        ///                 如果存在，则抛出异常
        ///                 如果不存在，则进行更新操作
        /// </summary>
        /// <param name="oper">操作人员</param>
        /// <param name="id">文件/文件夹ID</param>
        /// <param name="name">新的文件/文件夹名称</param>
        /// <returns></returns>
        public bool UpdateFileName(string oper, int id, string name)
        {
            tb_file entity = Get(id);
            if (entity == null)
            {
                throw new Exception($"当前文件/文件夹【{name}】不存在");
            }
            if (entity.name.Equals(name, StringComparison.CurrentCultureIgnoreCase))
            {
                return true;
            }
            int count = _dal.GetCount(p => p.upid == entity.upid && p.name == name && p.dir == oper);
            if (count > 0)
            {
                throw new Exception($"文件/文件夹【{name}】已存在");
            }
            _dal.BeginTran();
            try
            {
                count = _dal.UpdateIncludeColumns(new tb_file()
                {
                    id = id,
                    name = name,
                    editer = oper,
                    edit_date = DateTime.Now
                }, "name", "editer", "edit_date");
                bool success = count == 1;
                if (!success)
                {
                    throw new Exception($"RN01：[{id}]重命名失败");
                }

                //更新子文件夹的虚拟目录

                //插入操作日志
                tb_file_log opLog = new tb_file_log()
                {
                    file_id = id,
                    oper = oper,
                    op_content = $"重命名[{entity.name}] -> [{name}]",
                    op_date = DateTime.Now
                };
                success = _fileLogBll.Insert(opLog);
                if (!success)
                {
                    throw new Exception($"RN04：[{id}]重命名失败");
                }

                _dal.CommitTran();
                return success;
            }
            catch (Exception)
            {
                _dal.RollbackTran();
                throw;
            }

        }

        /// <summary> 
        ///  删除记录及对应的文件--虚拟删除
        /// </summary> 
        /// <param name="oper">操作人员</param>
        /// <param name="id_arr">键值数组</param> 
        /// <returns></returns> 
        public bool DeleteForVirual(string oper, params int[] id_arr)
        {
            _dal.BeginTran();
            try
            {
                DeleteForVirual(oper, true, id_arr);
                _dal.CommitTran();
                return true;
            }
            catch (Exception)
            {
                _dal.RollbackTran();
                throw;
            }
        }

        /// <summary> 
        ///  删除记录及对应的文件--虚拟删除
        /// </summary> 
        /// <param name="oper">操作人员</param>
        /// <param name="can_restore">删除的文件是否可以恢复</param>
        /// <param name="id_arr">键值数组</param> 
        /// <returns></returns> 
        public void DeleteForVirual(string oper, bool can_restore, params int[] id_arr)
        {
            foreach (int id in id_arr)
            {
                tb_file file = Get(id);
                if (file == null)
                {
                    throw new Exception($"DEL04：{id} 文件/文件夹不存在");
                }
                //删除当前记录及下级记录
                DeleteByUpidForVirual(oper, file, id, can_restore);
                //删除后，更新上级文件夹的大小
                bool success = UpdateUpFolderSize(oper, file.upid.Value, DateTime.Now);
                if (!success)
                {
                    throw new Exception($"DEL04：[{id}]删除失败");
                }
            }
        }

        /// <summary>
        /// 删除记录
        /// </summary>
        /// <param name="id">记录ID</param>
        /// <returns></returns>
        public bool DeleteById(int id)
        {
            return _dal.Delete(id);
        }

        /// <summary>
        /// 虚拟删除
        /// 根据上级ID删除对应的文件记录
        /// </summary>
        /// <param name="oper">操作人员</param>
        /// <param name="entity">删除的文件信息</param>
        /// <param name="delete_main_id">删除的主ID</param>
        /// <param name="can_restore">是否可能恢复</param>
        private void DeleteByUpidForVirual(string oper, tb_file entity, int delete_main_id, bool can_restore)
        {
            //如果是文件夹，则要删除其下级文件/文件夹
            if (entity.type == 1)
            {
                List<tb_file> fileList = List("", entity.id, null, null, null);
                foreach (tb_file file in fileList)
                {
                    DeleteByUpidForVirual(oper, file, delete_main_id, can_restore);
                }
            }

            //把文件/文件夹信息插入到预删除表中
            tb_file_predelete preDeleteFile = new tb_file_predelete()
            {
                id = entity.id,
                upid = entity.upid,
                name = entity.name,
                type = entity.type,
                size = entity.size,
                dir = entity.dir,
                level = entity.level,
                can_restroe = (ushort)(can_restore ? 1 : 0),
                delete_id = delete_main_id,
                creater = entity.creater,
                create_date = entity.create_date,
                editer = entity.editer,
                edit_date = entity.edit_date,

                virtual_dir = GetVirtualDir(entity.upid.Value),
                deleter = oper,
                delete_date = DateTime.Now
            };
            bool success = _preDeleteBll.Insert(preDeleteFile);
            if (!success)
            {
                throw new Exception($"DEL01：[{entity.id}]删除失败");
            }

            //删除当前文件/文件夹记录
            success = _dal.Delete(entity.id.Value);
            if (!success)
            {
                throw new Exception($"DEL02：[{entity.id}]删除失败");
            }

            //把当前操作插入操作日志中
            tb_file_log opLog = new tb_file_log()
            {
                file_id = entity.id,
                oper = oper,
                op_content = $"删除了{(entity.type == 1 ? "文件夹" : "文件")}",
                op_date = DateTime.Now
            };
            success = _fileLogBll.Insert(opLog);
            if (!success)
            {
                throw new Exception($"DEL03：[{entity.id}]删除失败");
            }
        }

        /// <summary>
        /// 判断要还原的文件/文件夹在原有的记录中是否存
        /// </summary>
        /// <param name="id">要还原的文件/文件夹ID</param>
        /// <returns>
        /// 返回有重复记录的原记录中的记录信息
        /// </returns>
        public List<KeyValuePair<int, string>> CheckFilesExists(int id)
        {
            List<KeyValuePair<int, string>> retList = new List<KeyValuePair<int, string>>();
            List<tb_file_predelete> deleteFiles = _preDeleteBll.List(null, null, null, null, id, false);
            foreach (tb_file_predelete file in deleteFiles)
            {
                tb_file fileModel = GetByVirtualDir(file);
                if (fileModel != null && fileModel.type == 2)
                {
                    retList.Add(new KeyValuePair<int, string>(file.id.Value, file.name));
                }
            }
            return retList;
        }

        /// <summary>
        /// 还原文件/文件夹
        /// </summary>
        /// <param name="oper">操作人员</param>
        /// <param name="idList">文件/文件夹id列表</param>
        /// <param name="userDealTypes">用户选择的处理方式</param>
        /// <returns></returns>
        public bool RestoreFiles(string oper, IEnumerable<int> idList, List<PubKeyValue<int, UserDealFileTypeEnum>> userDealTypes)
        {
            _dal.BeginTran();
            try
            {
                foreach (int id in idList)
                {
                    tb_file_predelete entity = _preDeleteBll.Get(id);
                    RestoreFile(oper, entity, userDealTypes);

                    if (entity.type == 1)
                    {
                        //把主删除ID下的子文件/子文件夹还原
                        List<tb_file_predelete> list = _preDeleteBll.List(null, null, null, null, entity.id.Value, false);
                        foreach (var model in list)
                        {
                            RestoreFile(oper, model, userDealTypes);
                        }
                    }

                    //把当前操作插入操作日志中
                    tb_file_log opLog = new tb_file_log()
                    {
                        file_id = entity.id,
                        oper = oper,
                        op_content = $"还原了{(entity.type == 1 ? "文件夹" : "文件")}：{userDealTypes.Find(p => p.Key == id)?.Value}",
                        op_date = DateTime.Now
                    };
                    bool success = _fileLogBll.Insert(opLog);
                    if (!success)
                    {
                        throw new Exception($"RESTORE03：[{entity.id}]还原失败");
                    }
                }

                _dal.CommitTran();
                return true;
            }
            catch (Exception)
            {
                _dal.RollbackTran();
                throw;
            }
        }

        /// <summary>
        /// 还原文件/文件夹
        /// </summary>
        /// <param name="oper">操作人员</param>
        /// <param name="entity">要还原的文件/文件夹</param>
        /// <param name="userDealTypes">用户选择的处理方式</param>
        /// <returns></returns>
        public void RestoreFile(string oper, tb_file_predelete entity, List<PubKeyValue<int, UserDealFileTypeEnum>> userDealTypes)
        {
            if (entity == null)
            {
                throw new Exception($"RESTORE01:要还原的文件不存在");
            }

            /***
             * 判断上级文件夹是否存在，如果不存在，则直接建立，
             *                         如果存在，则判断存在文件夹的ID是否是当前文件/文件夹的UPID
             *                                   如果相同则不用处理了，
             *                                   如果不相同，则要把当前文件/文件夹的UPID更新成存在的上级文件夹ID
             * 判断当前文件/文件夹名称是否存在
             * 1、如果存在，如是文件夹，则不用处理
             *              如是文件，则根据用户选择的处理方式进行处理
             * 2、如果不存在，则直接建立
             * */
            tb_file folder = new tb_file();
            if (string.IsNullOrEmpty(entity.virtual_dir))
            {
                folder = new tb_file()
                {
                    id = 0
                };
            }
            else
            {
                folder = GetOrCreateDir(oper, entity.dir, entity.virtual_dir);
            }
            tb_file model = GetByName(folder.id.Value, entity.name);
            bool success = false;
            bool deletedFileRestore = true; //垃圾筒中的文件是否要删除，默认删除
            bool insertToFile = false; //是否要把垃圾筒中的记录插入到tb_file中，默认不插入
            if (model != null)
            {
                if (entity.type != 1)
                {
                    //抓取用户选择的处理方式
                    UserDealFileTypeEnum dealType = userDealTypes.Find(x => x.Key == entity.id.Value).Value;
                    switch (dealType)
                    {
                        case UserDealFileTypeEnum.Rename:
                            entity.name = GetNewFileName(entity.upid.Value, entity.name, entity.type.Value);
                            insertToFile = true;
                            break;
                        case UserDealFileTypeEnum.Jump:
                            deletedFileRestore = false;
                            break;
                        case UserDealFileTypeEnum.Cover:
                            //把目标地的文件删除，要把can_restore设定为 (0-不能还原)，因为覆盖后的文件不能恢复
                            DeleteForVirual(oper, false, model.id.Value);
                            entity.upid = model.upid;
                            insertToFile = true;
                            break;
                        default:
                            throw new Exception("RESTORE09:用户没有选择重复文件处理方式");
                    }
                }
            }
            else
            {
                insertToFile = true;
            }

            if (insertToFile)
            {
                //把垃圾筒里的记录插入到tb_file中
                tb_file_restore file = new tb_file_restore()
                {
                    id = entity.id,
                    upid = folder.id,
                    name = entity.name,
                    size = entity.size,
                    type = entity.type,
                    dir = entity.dir,
                    level = entity.level,
                    creater = entity.creater,
                    create_date = entity.create_date,
                    editer = entity.editer,
                    edit_date = entity.edit_date
                };
                success = _restoreDal.Insert(file);
                if (!success)
                {
                    throw new Exception("RESTORE04:还原失败");
                }

                if (entity.type != 1)
                {
                    //还原后，更新上级文件夹的大小
                    success = UpdateUpFolderSize(oper, file.upid.Value, DateTime.Now);
                    if (!success)
                    {
                        throw new Exception($"RESTORE05:还原失败");
                    }
                }
            }

            //如果垃圾筒中的文件确认要删除，则重垃圾筒表中删除此记录
            if (deletedFileRestore)
            {
                success = _preDeleteBll.Delete(entity.id);
                if (!success)
                {
                    throw new Exception("RESTORE02:还原失败");
                }
            }
        }

        /// <summary>
        /// 移动指定文件/文件夹到目标文件夹
        /// </summary>
        /// <param name="oper">操作人员</param>
        /// <param name="moveId">要移动的文件/文件夹ID</param>
        /// <param name="targetId">目标文件夹ID</param>
        public bool MoveTo(string oper, int moveId, int targetId)
        {
            DateTime dtNow = DateTime.Now;
            tb_file moveObj = Get(moveId);
            if (moveObj == null)
            {
                throw new Exception($"要移动的{(moveObj.type == 1 ? "文件夹" : "文件")}不存在，请确认后再试");
            }
            tb_file targetObj;
            if (targetId == 0)
            {
                targetObj = new tb_file()
                {
                    id = 0,
                    name = "根目录"
                };
            }
            else
            {
                targetObj = Get(targetId);
                if (targetObj == null)
                {
                    throw new Exception("目标文件夹不存在");
                }
                if (targetObj.type != 1)
                {
                    throw new Exception("目标不是文件夹，请确认后再试");
                }
            }
            bool success = false;
            _dal.BeginTran();
            try
            {
                int oldUpid = moveObj.upid.Value;
                moveObj.upid = targetObj.id;
                moveObj.editer = oper;
                moveObj.edit_date = dtNow;
                success = _dal.UpdateIncludeColumns(moveObj, "upid", "editer", "edit_date") == 1;
                if (!success)
                {
                    throw new Exception("MOVE03：移动失败");
                }
                //添加移动日志
                tb_file_log opLog = new tb_file_log()
                {
                    file_id = moveObj.id,
                    oper = oper,
                    op_date = moveObj.edit_date,
                    op_content = $"移动到了文件夹【{targetObj.name}】"
                };
                success = _fileLogBll.Insert(opLog);
                if (!success)
                {
                    throw new Exception("MOVE04：移动失败");
                }

                //更新原文件夹的大小
                if (oldUpid != 0)
                {
                    //如果要移动的文件及文件夹不在根目录，则要更新所在文件夹的大小
                    success = UpdateUpFolderSize(oper, oldUpid, dtNow);
                    if (!success)
                    {
                        throw new Exception("MOVE01：移动失败");
                    }
                }

                //更新目标文件夹的大小
                success = UpdateUpFolderSize(oper, moveObj.upid.Value, dtNow);
                if (!success)
                {
                    throw new Exception("MOVE02：移动失败");
                }

                _dal.CommitTran();
                success = true;
            }
            catch (Exception)
            {
                _dal.RollbackTran();
                throw;
            }
            return success;
        }

        /// <summary> 
        /// 获得要导出的数据的Excel流 
        /// </summary> 
        /// <param name="upid">上级键值</param> 
        /// <param name="name">名称</param> 
        /// <param name="type">1：文件夹 2：文件</param> 
        /// <returns></returns> 
        public byte[] Export(int? upid, string name, int? type)
        {
            List<tb_file> result = List("", upid, name, type, null);
            return _excel.Collection2ExcelBytes(result, "Sheet1");
        }

        #endregion

        #region 扩展

        /// <summary>
        /// 创建压缩文件
        /// </summary>
        /// <param name="basePath">文件/文件夹的要目录</param>
        /// <param name="id">文件/文件夹ID</param>
        /// <returns></returns>
        public async Task<byte[]> CreateZipArchive(string basePath, int id)
        {
            tb_file root = Get(id);
            using (MemoryStream compressStream = new MemoryStream())
            {
                using (var zipArchive = new ZipArchive(compressStream, ZipArchiveMode.Create))
                {
                    await AddToZipArchive(basePath, root, root.name, zipArchive);
                }
                return compressStream.ToArray();
            }
        }

        /// <summary>
        /// 创建压缩文件
        /// </summary>
        /// <param name="basePath">文件/文件夹的要目录</param>
        /// <param name="idArr">文件/文件夹ID数组</param>
        /// <returns></returns>
        public async Task<byte[]> CreateZipArchive(string basePath, int[] idArr)
        {
            List<tb_file> fileList = _dal.CreateQueryable().Where(p => idArr.Contains(p.id.Value)).ToList();
            using (MemoryStream compressStream = new MemoryStream())
            {
                using (var zipArchive = new ZipArchive(compressStream, ZipArchiveMode.Create))
                {
                    foreach (tb_file file in fileList)
                    {
                        if (file.type == 1)
                        {
                            await AddToZipArchive(basePath, file, file.name, zipArchive);
                        }
                        else
                        {
                            await AddFileToZipArchive(basePath, file, "", zipArchive);
                        }
                    }
                }
                return compressStream.ToArray();
            }
        }

        /// <summary>
        /// 级联把文件夹加入压缩档
        /// </summary>
        /// <param name="basePath">根目录</param>
        /// <param name="folder">要加入压缩档的文件夹</param>
        /// <param name="path">加入压缩档的虚拟目录</param>
        /// <param name="zip">压缩档对象</param>
        /// <returns></returns>
        private async Task AddToZipArchive(string basePath, tb_file folder, string path, ZipArchive zip)
        {
            List<tb_file> subList = List("", folder.id, null, null, null);
            foreach (tb_file sub in subList)
            {
                string curPath = string.IsNullOrWhiteSpace(path) ? sub.name : $"{path}\\{sub.name}";
                if (sub.type == 1)
                {
                    await AddToZipArchive(basePath, sub, curPath, zip);
                }
                else
                {
                    await AddFileToZipArchive(basePath, sub, path, zip);
                }
            }
        }

        /// <summary>
        /// 把文件加入压缩档
        /// </summary>
        /// <param name="basePath">根目录</param>
        /// <param name="file">要加入压缩档的文件</param>
        /// <param name="path">加入压缩档的虚拟目录</param>
        /// <param name="zip">压缩档对象</param>
        /// <returns></returns>
        private async Task AddFileToZipArchive(string basePath, tb_file file, string path, ZipArchive zip)
        {
            string curPath = string.IsNullOrWhiteSpace(path) ? file.name : $"{path}\\{file.name}";
            ZipArchiveEntry zipEntry = zip.CreateEntry(curPath);
            using var entryStream = zipEntry.Open();
            FileInfo fi = new FileInfo($"{basePath}\\{file.dir}\\{file.id}");
            using var zipToStream = fi.OpenRead();
            await zipToStream.CopyToAsync(entryStream);
        }

        /// <summary>
        /// 获得文件/文件夹的虚拟目录
        /// </summary>
        /// <param name="id">文件/文件的ID</param>
        /// <returns></returns>
        public string GetVirtualDir(int id)
        {
            string dir = string.Empty;
            tb_file model = Get(id);
            if (model != null)
            {
                string temp = GetVirtualDir(model.upid.Value);
                if (!string.IsNullOrEmpty(temp))
                {
                    dir = $"{temp}\\{model.name}";
                }
                else
                {
                    dir = model.name;
                }
            }
            return dir;
        }

        /// <summary>
        /// 级联判断目录是否存在，如果不存在，则要建立一个新的目录
        /// </summary>
        /// <param name="oper">操作人员</param>
        /// <param name="dir">目录</param>
        /// <param name="virtualDir">虚拟目录</param>
        /// <returns>
        /// 返回上级目录
        /// </returns>
        public tb_file GetOrCreateDir(string oper, string dir, string virtualDir)
        {
            DateTime dtNow = DateTime.Now;
            IEnumerable<string> dirs = virtualDir.Split('\\');
            int dirCount = dirs.Count();
            tb_file file = null;
            int upid = 0;
            for (int i = 0; i < dirCount; i++)
            {
                if (i > 0)
                {
                    upid = file.id.Value;
                }
                file = List(oper, upid, dirs.ElementAt(i), null, null).FirstOrDefault();
                if (file == null)
                {
                    file = new tb_file()
                    {
                        upid = upid,
                        name = dirs.ElementAt(i),
                        type = 1,
                        size = 0,
                        dir = dir,
                        creater = oper,
                        create_date = dtNow,
                        editer = oper,
                        edit_date = dtNow
                    };
                    int id = _dal.InsertReturnIdentity(file);
                    file.id = id;
                }
            }
            return file;
        }

        /// <summary>
        /// 获得重复名字的文件/文件夹的新名称
        /// </summary>
        /// <param name="upid">上级文件夹ID</param>
        /// <param name="name">文件/文件夹名称</param>
        /// <param name="type">
        /// 1：文件夹 2：文件
        /// 默认为1 文件夹
        /// </param>
        /// <returns></returns>
        public string GetNewFileName(int upid, string name, int type = 1)
        {
            string tempName = name;
            string extension = string.Empty;
            if (type != 1)
            {
                tempName = System.IO.Path.GetFileNameWithoutExtension(name);
                extension = System.IO.Path.GetExtension(name);
            }
            var tempList = _dal.GetSimpleClient()
                               .GetList(p => p.upid == upid &&
                                             p.name.StartsWith(tempName) &&
                                             p.type == type);
            List<string> nameList = new List<string>();
            if (type != 1)
            {
                nameList = tempList.Where(p => System.IO.Path.GetExtension(p.name).Equals(extension, StringComparison.CurrentCultureIgnoreCase))
                                   .Select(p => System.IO.Path.GetFileNameWithoutExtension(p.name).Replace(tempName, "", StringComparison.CurrentCultureIgnoreCase))
                                   .Where(p => int.TryParse(p, out int test))
                                   .ToList();
            }
            else
            {
                nameList = tempList.Select(p => p.name.Replace(tempName, "", StringComparison.CurrentCultureIgnoreCase))
                                   .Where(p => int.TryParse(p, out int test))
                                   .ToList();
            }
            int maxIndex = 0;
            if (nameList.Count > 0)
            {
                maxIndex = nameList.Max(p => Convert.ToInt32(p));
            }
            return $"{tempName}{++maxIndex}{extension}";
        }

        #endregion
    }
}
